<?php

/*
 * 图片类，包括验证码、缩略图、裁剪、水印
 * @author php_angel <php_angel@126.com>
 * @date 2012-07-15
 */

class Image {
	
	//错误信息
	private $error = '';
	
	//图像资源
	private $im_source = array();
	
	public function __construct()
	{
		//gd库检查
		if (FALSE===($gd_version = $this->gd_version()))
		{
			$this->set_error('未安装gd库');
			return FALSE;
		}

		if (version_compare($gd_version, '2.0')<0)
		{
			$this->set_error('需要gd库版本大于2');
			return FALSE;
		}
	}

	/*
	 * 图片验证码
	 * @param $config array 设置参数
	 * @return void
	 */
	public function verify($config = array())
	{
		/*
		 * width integer 验证码图片宽度
		 * height integer 验证码图片高度
		 * length integer 验证码字符串长度
		 * size integer 字体大小像素
		 * mode integer 验证码字符串模式，1数字，2大字母，3大字母加数字，默认数字
		 * name string 验证码字段名称，保存如$_SESSION[$name] = md5($string)
		 * font string 字体路径
		 * background mixed 背景色，默认白色，rgb数组格式，如array(100,100,100) 其他格式一致为随机颜色
		 * color mixed 字体色，默认黑色，rgb数组格式，如array(100,100,100) 其他格式一致为随机颜色
		 * rotation integer 旋转角度 0-90范围
		 */
		$default = array(
			'width' => 80,
			'height' => 30,
			'length' => 4,
			'size' => 20,
			'mode' => 1,
			'name' => 'verify',
			'font' => 'Biometri.ttf',
			'background' => array(255, 255, 255),
			'color' => array(0, 0, 0),
			'rotation' => 30
		);

		$config = array_intersect_key($config, $default);
		$config = array_merge($default, $config);
		
		$width = $config['width'];
		$height = $config['height'];
		$length = $config['length'];
		$size = $config['size'];
		$mode = $config['mode'];
		$mode = in_array($mode, array(1,2,3)) ? $mode : 1;
		$name = trim($config['name']);
		$font = trim($config['font']);
		$background = $config['background'];
		$color = $config['color'];
		$rotation = $config['rotation']%90;
		
		if ( ! $background)
		{
			//（随机）
			$background = array(
				mt_rand(200, 255),
				mt_rand(200, 255),
				mt_rand(200, 255)
			);
		}
		
		//检测字体
		if ( ! file_exists($font))
		{
			$font = dirname(__FILE__).'/'.$font;
			if ( ! file_exists($font))
			{
				$font = '';
			}
		}
		
		$text = $this->rand_string($length, $mode);
		
		//DOIT 必要时需要修改
		$_SESSION[$config['name']] = md5($text);

		$im = $this->create($width, $height, 'png');
		
		if ( ! $im)
		{
			$this->set_error('创建图像失败');
			return FALSE;
		}
		
		//背景色
		$backColor = $this->color($im, $background);
		
		//画一个矩形框，填充背景色
		imagefilledrectangle($im, 0, 0, $width, $height, $backColor);
		
		//字符串颜色
		if ($color)
		{
			$stringColor = $this->color($im, $color);
		}
		else
		{
			$stringColor = $this->color($im, array(mt_rand(0,200), mt_rand(0,120), mt_rand(0,120)));
		}
		
		$i = 0;
		$x = ($width-($size-5)*$length)/2;
		if ($font)
		{
			for ($i; $i<$length; $i++)
			{
				imagettftext($im, $size, mt_rand(-$rotation, $rotation), $x, mt_rand(($height+$size)/2-5, ($height+$size)/2+5), $stringColor, $font, $text{$i});
				$x += $size-5;
			}
		}
		else
		{
			for ($i; $i<$length; $i++)
			{
				imagestring($im, 5, $x, mt_rand(($height-$size)/2-5, ($height-$size)/2+5), $text{$i}, $stringColor);
				$x += $size-5;
			}
		}

		$this->output($im, 'png');
	}
	
	/*
	 * 裁剪图片
	 * @access public
	 * @param $image 源图片地址
	 * @param $new_image string 保存新图片
	 * @param $width ingeter 裁剪宽度，默认130
	 * @param $height integer 裁剪高度，默认130
	 * @param $x integer 距左上角水平距离，默认0
	 * @param $y integer 距左上角垂直距离，默认0
	 * @param $quality integer 裁剪图片质量，0-100
	 * @return string|boolean
	 */
	public function cut($image, $new_image = '', $width = 130, $height = 130, $x = 0, $y = 0, $quality = 85)
	{
		if ( ! file_exists($image))
		{
			$this->set_error('源图片不存在');
			return FALSE;
		}
		
		if (FALSE===($info = $this->get_info($image)))
		{
			$this->set_error('源图片不是图片');
			return FALSE;
		}
		
		$image = str_replace('\\', '/', realpath($image));
		
		$src_im = $this->create_from($image);

		if ( ! $src_im)
		{
			$this->set_error('创建源图片图像失败');
			return FALSE;
		}

		$cut_im = $this->create($width, $height, $info['type']);
		
		if ( ! $cut_im)
		{
			$this->set_error('创建裁剪图像失败');
			return FALSE;
		}
		
		if (FALSE === imagecopy($cut_im, $src_im, $x, $y, 0, 0, $width, $height))
		{
			$this->set_error('拷贝图像失败');
			return FALSE;
		}
		
		//保存图片
		$new_image = $new_image ? str_replace('\\', '/', $new_image) : $image;

		if (FALSE === $this->save($info['type'], $cut_im, $new_image, $quality))
		{
			$this->set_error('保存图片失败');
			return FALSE;
		}
		
		if ( ! file_exists($new_image))
		{
			$this->set_error('裁剪图片失败');
			return FALSE;
		}
		
		return $new_image;
	}

	/*
	 * 生成缩略图
	 * @access public
	 * @param $image string 原图片文件地址
	 * @param $new_image string 缩略图文件地址
	 * @param $thumb_width integer 缩略图宽度
	 * @param $thumb_height integer 缩略图高度
	 * @param $quality integer 生成缩略图质量
	 * @return string|boolean
	 */
	public function thumb($image, $new_image = '', $thumb_width = 130, $thumb_height = 130, $quality = 85)
	{
		if ( ! file_exists($image))
		{
			$this->set_error('源图片不存在');
			return FALSE;
		}
		
		$image = str_replace('\\', '/', realpath($image));
		
		//获取原图信息
		if (FALSE === ($info = $this->get_info($image)))
		{
			$this->set_error('源图片不是图片');
			return FALSE;
		}
		
		$src_width  = $info['width'];
		$src_height = $info['height'];
		$type = $info['type'];
		
		//计算缩放比例
		$scale = min($thumb_width/$src_width, $thumb_height/$src_height);

		if ($scale>=1)
		{
			//超过原图大小不再缩略
			$thumb_width   =  $src_width;
			$thumb_height  =  $src_height;
		}
		else
		{
			//缩略图尺寸
			$thumb_width = $src_width*$scale;
			$thumb_height = $src_height*$scale;
		}
		
		//床架源图片图像
		$src_im = $this->create_from($image);
		
		if ( ! $src_im)
		{
			$this->set_error('创建源图片图像失败');
			return FALSE;
		}

		//创建缩略图图像
		$thumb_im = $this->create($thumb_width, $thumb_height, $type);
		
		if ( ! $thumb_im)
		{
			$this->set_error('创建缩略图图像失败');
			return FALSE;
		}

		//拷贝图像到缩略图
		if (function_exists('imagecopyresampled'))
		{
			if (FALSE === imagecopyresampled($thumb_im, $src_im, 0, 0, 0, 0, $thumb_width, $thumb_height, $src_width, $src_height))
			{
				$this->set_error('拷贝图片失败');
				return FALSE;
			}
		}
		else
		{
			if (FALSE === imagecopyresized($thumb_im, $src_im, 0, 0, 0, 0, $thumb_width, $thumb_height, $src_width, $src_height))
			{
				$this->set_error('拷贝图片失败');
				return FALSE;
			}
		}
		
		//将绿色定义为透明
		if ($type=='png' || $type=='gif')
		{
			$background_color = imagecolorallocate($thumb_im, 0, 255, 0);
			imagecolortransparent($thumb_im, $background_color);
		}
		
		//对jpeg图形设置隔行扫描
		if ($type=='jpg' || $type=='jpeg')
		{
			imageinterlace($thumb_im, 1);
		}
		
		//保存图片
		$new_image = $new_image ? str_replace('\\', '/', $new_image) : $image;
		
		if (FALSE === $this->save($type, $thumb_im, $new_image, $quality))
		{
			$this->set_error('保存图片失败');
			return FALSE;
		}
		
		if ( ! file_exists($new_image))
		{
			$this->set_error('生成缩略图失败');
			return FALSE;
		}
		
		return $new_image;
	}

	/*
	 * 添加水印
	 * @access public
	 * @param $image string 源图片地址
	 * @param $water string 水印图片地址
	 * @param $new_image string 新文件地址
	 * $param $x integer 距离源图片（大于0左上角）or（小于0右下角）水平方向距离，0为居中
	 * @param $y integer 距离源图片（大于0左上角）or（小于0右下角）垂直方向距离，0为居中
	 * @param $alpha integer 水印透明度
	 * @param $quality integer 保存图片质量
	 * @return string|boolean
	 */
	public function water($image, $water, $new_image = '', $x = -20, $y = -20, $alpha = 60, $quality = 85)
	{
		if ( ! file_exists($image))
		{
			$this->set_error('源图片不存在');
			return FALSE;
		}
		
		if ( ! file_exists($water))
		{
			$this->set_error('水印图片不存在');
			return FALSE;
		}
		
		$image = str_replace('\\', '/', realpath($image));
		$water = str_replace('\\', '/', realpath($water));
		
		//源图片信息
		if (FALSE === ($image_info = $this->get_info($image)))
		{
			$this->set_error('源图片不是图片');
			return FALSE;
		}
		
		//水印图片信息
		if (FALSE === ($water_info = $this->get_info($water)))
		{
			$this->set_error('水印不是图片');
			return FALSE;	
		}
		
		//仅支持png,gif格式水印图片
		if($water_info['type'] != 'png' && $water_info['type'] != 'gif')
		{
			$this->set_error('仅支持png、gif格式水印图片');
			return FALSE;
		}
		
		$image_im = $this->create_from($image);
		
		if ( ! $image_im)
		{
			$this->set_error('创建源图片图像失败');
			return FALSE;
		}
		
		$water_im = $this->create_from($water);
		
		if ( ! $water_im)
		{
			$this->set_error('创建水印图片图像失败');
			return FALSE;
		}
		
		//水印图片混色模式
		if (FALSE === imagealphablending($water_im, TRUE))
		{
			$this->set_error('设定水印图片混色模式失败');
			return FALSE;
		}
		
		//水印图片位置计算
		if ($x==0)
		{
			$x = ($image_info['width']-$water_info['width'])/2;
		}
		elseif ($x<0)
		{
			$x = $image_info['width']-$water_info['width']-abs($x);
		}
		$x = $x>=0 ? $x : 0;
		
		if ($y==0)
		{
			$y = ($image_info['height']-$water_info['height'])/2;
		}
		elseif ($y<0)
		{
			$y = $image_info['height']-$water_info['height']-abs($y);
		}
		$y = $y>=0 ? $y : 0;
		
		//拷贝水印到图片
		if (FALSE === imagecopymerge($image_im, $water_im, $x, $y, 0, 0, $water_info['width'], $water_info['height'], $alpha))
		{
			$this->set_error('拷贝图像失败');
			return FALSE;
		}
		
		$new_image = $new_image ? str_replace('\\', '/', $new_image) : $image;

		if (FALSE === $this->save($image_info['type'], $image_im, $new_image, $quality))
		{
			$this->set_error('保存图片失败');
			return FALSE;
		}
		
		if ( ! file_exists($new_image))
		{
			$this->set_error('添加水印失败');
			return FALSE;
		}
		
		return $new_image;
	}

	/*
	 * 图片信息
	 * @access public
	 * @param $image string 图片地址
	 * @return array|boolean
	 */
	public function get_info($image)
	{	
		if (FALSE!==($image_info = @getimagesize($image)))
		{
			switch ($image_info[2])
			{
				case 1:
					$type = 'gif';
					break;
				case 2:
					$type = 'jpg';
					break;
				case 3:
					$type = 'png';
					break;
				default :
					return FALSE;
			}
			
			$info = array(
				'width' => $image_info[0],
				'height' => $image_info[1],
				'type' => $type,
				'mime' => $image_info['mime']
			);
			
			return $info;
		}
		else
		{
			return FALSE;
		}
	}
	
	/*
	 * 新建空白图像（资源）
	 * @access public
	 * @param width integet 图像宽度
	 * @param $height integet 图像高度
	 * @param $type string 图像类型
	 * @return resource|boolean
	 */
	public function create($width, $height, $type = 'png')
	{
		$type = strtolower($type);
		
		//除了gif类型其余可以建立真彩色图像
		if ($type != 'gif' && function_exists('imagecreatetruecolor'))
		{
			$im = imagecreatetruecolor($width, $height);
		}
		else
		{
			$im = imagecreate($width, $height);
		}
		
		if ( ! $im)
		{
			return FALSE;
		}
		
		$this->im_source[] = & $im;

		return $im;
	}
	
	/*
	 * 从文件新建图像（资源）
	 * @access public
	 * @param image string 图片文件地址
	 * @return resource|boolean
	 */
	public function create_from($image)
	{
		if (FALSE === ($info = $this->get_info($image)))
		{
			return FALSE;
		}
		
		$type = $info['type']=='jpg' ? 'jpeg' : $info['type'];
		
		$fun = 'imagecreatefrom'.$type;
		$im = $fun($image);
		
		if ( ! $im)
		{
			return FALSE;
		}
		
		$this->im_source[] = & $im;
		
		return $im;
	}
	
	/*
	 * 已定义的图片格式，调用image方法，将图片资源保存为图片
	 * @access protected
	 * @param $type string 图片格式
	 * @param $im 图片资源
	 * @param $new_image string 新图片地址
	 * @param $quality 保存图片质量，不同版本可能写不同，那么就无效了
	 * @return boolean
	 */
	protected function save($type, $im, $new_image, $quality)
	{
		$type = $type=='jpg' ? 'jpeg' : $type;
		switch ($type)
		{
			case 'png':
				//版本大于5.1.2的时候，qualtity的范围变为0-9
				if (version_compare(PHP_VERSION, '5.1.2')>0)
				{
					$quality = floor($quality/10);
					if ($quality == 10)
					{
						$quality = 9;
					}
				}
				break;
			case 'jpeg':
			case 'jpg':
				break;
			default:
				unset($quality);
				break;
		}
		
		$fun = 'image'.$type;
		if (isset($quality))
		{
			return $fun($im, $new_image, $quality);
		}
		else
		{
			return $fun($im, $new_image);
		}
	}

	/*
	 * 随机字符串
	 * @access protected
	 * @param $len integer 返回字符串长度
	 * @param $type integer 返回字符串类型方式
	 * @return string
	 */
	protected function rand_string($len = 4, $type = 1)
	{
		$str = '';

		switch($type)
		{
			case 1:
				$chars = str_repeat('0123456789', 3);
				break;
			case 2:
				$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
				break;
			case 3:
				$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ012345679';
				break;
			default :
				$chars = str_repeat('0123456789', 3);
				break;
		}
		
		if($len>10)
		{
			//位数过长重复字符串一定次数
			$chars= $type==1 ? str_repeat($chars, $len) : str_repeat($chars, 5);
		}
		
		//打乱顺序
		$chars = str_shuffle($chars);
		$str = substr($chars, 0, $len);
		
		return $str;
	}


	/*
	 * 输出图像到浏览器
	 * @access public
	 * @param $im resource 图像资源
	 * @param $type string 图像类型
	 * @return void
	 */
	public function output($im, $type = 'png')
	{
		if ( ! is_resource($im))
		{
			return;
		}
		
		$type = strtolower($type);
		$type = $type=='jpg' ? 'jpeg' : $type;
		
		//输出图像
		header("Content-type: image/".$type);
		$fun='image'.$type;
		$fun($im);
		return;
	}
	
	/*
	 * gd库版本
	 * @access public
	 * @return string|boolean
	 */
	public function gd_version()
	{
		static $_gd_version = NULL;
		
		if (isset($_gd_version))
		{
			return $_gd_version;
		}
		
		//是否安装gd库
		if ( ! function_exists('gd_info'))
		{
			$_gd_version = FALSE;
			return FALSE;
		}
		
		$info = gd_info();
		$_gd_version = preg_replace('/[^0-9\.]/', '', $info['GD Version']);
		return $_gd_version;
	}
	
	/*
	 * 为图像分配颜色
	 * @access protected
	 * @param $im resource 图像资源
	 * @param $color array|string array(0,0,0)或'0,0,0'
	 * @return integer
	 */
	protected function color($im, $color = array(0, 0, 0))
	{
		if ( ! is_resource($im))
		{
			return -1;
		}

		if (is_string($color))
		{
			$color = implode(',', $color);
		}
		
		if ( ! is_array($color) || count($color) != 3)
		{
			exit('color参数不正确');
		}
		
		return imagecolorallocate($im, intval($color[0]), intval($color[1]), intval($color[2]));
	}
	
	/*
	 * 设置错误信息
	 * @access protected
	 * @param $error string 错误信息
	 * @return void
	 */
	protected function set_error($error)
	{
		$this->error = trim($error);
		return;
	}

	/*
	 * 返回错误信息
	 * @access public
	 * @return string
	 */
	public function get_error()
	{
		return $this->error;
	}

	/*
	 * 释放图像资源
	 * @access public
	 * @param $im resource 图像资源
	 * @return void
	 */
	public function free(& $im)
	{
		if ($im && is_resource($im))
		{
			imagedestroy($im);
		}
		return;
	}

	/*
	 * @access public
	 * 结束后从内存中销毁图像资源
	 */
	public function __destruct()
	{
		if ($this->im_source)
		{
			foreach ($this->im_source as $im)
			{
				$this->free($im);
			}
		}
	}

}